package me.fanchaoo.web.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.MACSigner;
import lombok.extern.slf4j.Slf4j;
import me.fanchaoo.constant.JwtConstant;
import me.fanchaoo.constant.RedisPrefixConst;
import me.fanchaoo.dbo.*;
import me.fanchaoo.enums.EnumProvider;
import me.fanchaoo.exception.BusinessException;
import me.fanchaoo.util.*;
import me.fanchaoo.web.dao.mapper.*;
import me.fanchaoo.web.dto.LoginUserDTO;
import me.fanchaoo.web.request.*;
import me.fanchaoo.web.service.LoginService;
import me.fanchaoo.web.util.*;
import me.fanchaoo.web.util.phonesms.PhoneMsmUtils;
import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
public class LoginServiceImpl implements LoginService {


    @Resource
    private UserQueryMapper userQueryMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private ArtistQueryMapper artistQueryMapper;

    @Autowired
    private RedisUtils redisUtils;
    @Resource
    private UserSettingMapper userSettingMapper;
    @Resource
    private UserSettingQueryMapper userSettingQueryMapper;

    @Autowired
    private EmailUtils emailUtils;
    @Autowired
    private  PhoneMsmUtils  phoneMsmUtils;

    @Autowired
    private IpUtils ipUtils;
    @Override
    @Transactional
    public LoginUserDTO login(LoginRequest request) throws Exception {

        ArtistDBO artistDBO = artistQueryMapper.getByRandomKey(request.getRandomKey());
        AssertUtils.notNull(artistDBO, "未找到appId配置");

        String appId = "";
        String appSecret = "";
        JSONObject unionInfo = null;
        EnumProvider provider = request.getProvider();
        if (Objects.equals(provider, EnumProvider.weixin)) {
            appId = artistDBO.getMpWeixinAppId();
            appSecret = artistDBO.getMpWeixinAppSecret();
            String sessionKey = getSessionKey(provider, request.getCode(), appId, appSecret);
            unionInfo = JSON.parseObject(WeixinUtils.decryptData(request.getEncryptedData(), sessionKey, request.getIv()));
            UserDBO userDBO = addOrUpdateUser(provider, request.getUserInfo(), unionInfo, artistDBO.getId());
            return createToken(userDBO, provider,artistDBO);
        }
        if (Objects.equals(provider, EnumProvider.qq)) {
            appId = artistDBO.getMpQqAppId();
            appSecret = artistDBO.getMpQqAppSecret();
            String sessionKey = getSessionKey(provider, request.getCode(), appId, appSecret);
            unionInfo = JSON.parseObject(WeixinUtils.decryptData(request.getEncryptedData(), sessionKey, request.getIv()));
            UserDBO userDBO = addOrUpdateUser(provider, request.getUserInfo(), unionInfo, artistDBO.getId());
            return createToken(userDBO, provider,artistDBO);
        }

        throw new BusinessException("你为什么要登陆！");
    }

      public LoginUserDTO emailLogin(EmailLoginRequest request) throws Exception {
          String username=request.getUsername();
        ArtistDBO artistDBO = artistQueryMapper.getByRandomKey(request.getRandomKey());
          EnumProvider provider =  request.getProvider();

          UserDBO userDBO ;
          if(username.contains("@")) {
              userDBO = userQueryMapper.getByEmail(artistDBO.getId(),username);
          }else {
              userDBO = userQueryMapper.getByPhone(artistDBO.getId(),username);
          }
        if (Objects.isNull(userDBO)) {
            throw new BusinessException("用户不存在");
        }else {
         if(!MD5Util.code(request.getPassword()).equals(userDBO.getPassword())){
             throw new BusinessException("密码错误");
          }
        }
          //获取请求
          RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
          ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
          HttpServletRequest requestIp = servletRequestAttributes.getRequest();
          String  ipAddress = ipUtils.getIpAddress(requestIp);
          String  ipSource =  ipUtils.getIpSource(ipAddress);
        insetUserSetting(artistDBO.getId(),userDBO.getId());
          userDBO.setIpAddress(ipAddress);
          userDBO.setIpSource(ipSource);
          userMapper.updateByPrimaryKeySelective(userDBO);
        return createToken(userDBO,provider,artistDBO);
    }

    public void insetUserSetting(Long artistId ,long userId){
        UserSettingDBO  userSettingDBOQuery =  userSettingQueryMapper.getByUserId(artistId,userId);
        if (Objects.isNull(userSettingDBOQuery)) {
            UserSettingDBO userSettingDBO = new UserSettingDBO();
            userSettingDBO.setUserId(userId);
            userSettingDBO.setArtistId(artistId);
            userSettingMapper.insertSelective(userSettingDBO);
        }
    }
    public void getMailCode(MailCodeRequest request) throws Exception{
        String username=request.getUsername();
        if (Objects.isNull(redisUtils.getString(RedisPrefixConst.USER_CODE_KEY+username))) {
            // 生成六位随机验证码发送
            String code = CommonUtils.getRandomCode();
            if(username.contains("@")){
                // 发送验证码
                EmailDBO emailDTO = EmailDBO.builder()
                        .email(username)
                        .subject("验证码")
                        .content("您的验证码为 " + code + " 有效期3分钟，请不要告诉他人哦！")
                        .build();

                emailUtils.send(emailDTO);
            }else {
                Map<String,Object> param = new HashMap<>();
                param.put("code", code);
                phoneMsmUtils.send(param,username);
            }
            // 将验证码存入redis，设置过期时间为3分钟
            redisUtils.set(RedisPrefixConst.USER_CODE_KEY+username, code, RedisPrefixConst.CODE_EXPIRE_TIME);
        }else {
            throw new BusinessException(redisUtils.getExpire(RedisPrefixConst.USER_CODE_KEY+username)+"秒后可重新发送!");
        }

    }
    public void register(RegisterRequest registerData){
        String username=registerData.getUsername();
          // 校验账号是否合法
        checkUser(registerData);

        ArtistDBO artistDBO = artistQueryMapper.getByRandomKey(registerData.getRandomKey());
        //获取请求
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String  ipAddress = ipUtils.getIpAddress(request);
        String  ipSource =  ipUtils.getIpSource(ipAddress);
        UserDBO userDBO = new UserDBO();
        userDBO.setName(registerData.getName());
        userDBO.setArtistId(artistDBO.getId());
        if(username.contains("@")) {
            userDBO.setEmail(username);
        }else {
            userDBO.setPhone(username);
        }
        userDBO.setPassword (MD5Util.code(registerData.getPassword()));
        userDBO.setIpAddress(ipAddress);
        userDBO.setIpSource(ipSource);
        userMapper.insertSelective(userDBO);
        UserDBO newUserDBO;
        if(username.contains("@")) {
          newUserDBO = userQueryMapper.getByEmail(artistDBO.getId(),username);
        }else {
          newUserDBO = userQueryMapper.getByPhone(artistDBO.getId(),username);
        }
        insetUserSetting(artistDBO.getId() ,newUserDBO.getId());
    }
    public void  loginOut() {
//        LoginUserDTO loginUser = UserContext.getLoginUser();
//        String  artistName = loginUser.getArtistName();
//        String userKey = artistName + RedisPrefixConst.LOGIN_USER + ":" + loginUser.getUserId();
//        redisUtils.deleteByKey(userKey);
    }
    public void getPassword(RegisterRequest request){
        String username=request.getUsername();
        if (!request.getCode().equals(redisUtils.getString(RedisPrefixConst.USER_CODE_KEY+username))) {
            throw new BusinessException("验证码错误！");
        }
        ArtistDBO artistDBO = artistQueryMapper.getByRandomKey(request.getRandomKey());
        UserDBO userDBO ;
        if(username.contains("@")) {
            userDBO = userQueryMapper.getByEmail(artistDBO.getId(),username);
        }else {
            userDBO = userQueryMapper.getByPhone(artistDBO.getId(),username);
        }

        if (Objects.isNull(userDBO)) {
            throw new BusinessException("用户不存在");
        }else {
            userDBO.setPassword (MD5Util.code(request.getPassword()));
            userMapper.updateByPrimaryKeySelective(userDBO);
        }
    }
    /**
     * 校验用户数据是否合法
     *
     * @param user 用户数据
     * @return 结果
     */
    private void checkUser(RegisterRequest request) {
        String username=request.getUsername();
//        if (!request.getCode().equals(redisUtils.getString(RedisPrefixConst.USER_CODE_KEY+username))) {
//            throw new BusinessException("验证码错误！");
//        }
        //查询用户名是否存在
        ArtistDBO artistDBO = artistQueryMapper.getByRandomKey(request.getRandomKey());
        UserDBO userDBO;
        if(username.contains("@")) {
         userDBO = userQueryMapper.getByEmail(artistDBO.getId(), username);
            if (Objects.nonNull(userDBO)) {
                throw new BusinessException("邮箱已被注册！");
            }
        }else {
          userDBO = userQueryMapper.getByPhone(artistDBO.getId(), username);
            if (Objects.nonNull(userDBO)) {
                throw new BusinessException("手机号已被注册！");
            }
        }
    }

    private UserDBO addOrUpdateUser(EnumProvider provider, LoginRequest.UserInfo userInfo, JSONObject unionInfo, Long artistId) {

        UserDBO userDBO = null;
        if (Objects.equals(provider, EnumProvider.weixin)) {
            userDBO = userQueryMapper.getByMiniWeixinUnionId(artistId, unionInfo.getString("unionId"), unionInfo.getString("openId"));
        }
        if (Objects.equals(provider, EnumProvider.qq)) {
            userDBO = userQueryMapper.getByMiniQQUnionId(artistId, unionInfo.getString("unionId"), unionInfo.getString("openId"));
        }
        //获取请求
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String  ipAddress = ipUtils.getIpAddress(request);
        String  ipSource =  ipUtils.getIpSource(ipAddress);
        if (Objects.isNull(userDBO)) {
            userDBO = new UserDBO();
            userDBO.setArtistId(artistId);
            userDBO.setName(userInfo.getNickName());
            userDBO.setGender(userInfo.getGender());
            userDBO.setAvatarUrl(userInfo.getAvatarUrl());
            userDBO.setCountry(userInfo.getCountry());
            userDBO.setProvince(userInfo.getProvince());
            userDBO.setCity(userInfo.getCity());
            userDBO.setIpAddress(ipAddress);
            userDBO.setIpSource(ipSource);
            if (Objects.equals(provider, EnumProvider.weixin)) {
                userDBO.setMiniWeixinUnionId(unionInfo.getString("unionId"));
                userDBO.setMiniWeixinOpenId(unionInfo.getString("openId"));
            }
            if (Objects.equals(provider, EnumProvider.qq)) {
                userDBO.setMiniQqUnionId(unionInfo.getString("unionId"));
                userDBO.setMiniQqOpenId(unionInfo.getString("openId"));
            }

            userMapper.insertSelective(userDBO);
        } else {
//            userDBO.setGender(userInfo.getGender());
//            userDBO.setAvatarUrl(userInfo.getAvatarUrl());
//            userDBO.setCountry(userInfo.getCountry());
//            userDBO.setProvince(userInfo.getProvince());
//            userDBO.setCity(userInfo.getCity());
            userDBO.setIpAddress(ipAddress);
            userDBO.setIpSource(ipSource);
            userMapper.updateByPrimaryKeySelective(userDBO);
        }

        return userDBO;
    }


    private String getSessionKey(EnumProvider provider, String jsCode, String appId, String appSecret) {

        String qqUrl = "https://api.q.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
        String weixinUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + jsCode + "&grant_type=authorization_code";

        String url = "";
        if (Objects.equals(provider, EnumProvider.weixin)) {
            url = String.format(weixinUrl, appId, appSecret, jsCode);
        }
        if (Objects.equals(provider, EnumProvider.qq)) {
            url = String.format(qqUrl, appId, appSecret, jsCode);
        }
        String unionInfoStr = HttpUtils.getJSON(url);

        JSONObject openInfo = JSON.parseObject(unionInfoStr);
        String errCode = openInfo.getString("errcode");
        if (!(StringUtils.isBlank(errCode) || Objects.equals(errCode, "0"))) {
            throw new BusinessException("获取unionId出错：" + unionInfoStr);
        }
        return openInfo.getString("session_key");
    }

    public LoginUserDTO createToken(UserDBO userDBO, EnumProvider provider, ArtistDBO  artistDBO) throws Exception {

//        ArtistDBO artistDBO = artistQueryMapper.selectByPrimaryKey(userDBO.getArtistId());
        String artistName = artistDBO.getName();

        String unionId = "";
        if (Objects.equals(provider, EnumProvider.weixin)) {
            unionId = userDBO.getMiniWeixinUnionId();
        }
        if (Objects.equals(provider, EnumProvider.qq)) {
            unionId = userDBO.getMiniQqUnionId();
        }

        net.minidev.json.JSONObject jsonObject = new net.minidev.json.JSONObject();
        jsonObject.put("iss", "me.fanchaoo");
        jsonObject.put("iat", Calendar.getInstance().getTimeInMillis());

        // 自定义属性
        jsonObject.put("artistId", userDBO.getArtistId());
        jsonObject.put("artistName", artistDBO.getName());
        jsonObject.put("userId", userDBO.getId());
        jsonObject.put("userName", userDBO.getName());
        jsonObject.put("unionId", unionId);
        jsonObject.put("provider", provider);

        // 生成token串
        JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256), new Payload(jsonObject));
        byte[] sharedKey = JwtConstant.SIGNATURE.getBytes();
        jwsObject.sign(new MACSigner(sharedKey));
        String tokenStr = jwsObject.serialize();

        // 添加到redis
        String userKey = artistName + RedisPrefixConst.LOGIN_USER+ provider + ":" + userDBO.getId();
        String tokenMd5 = MD5Utils.md5Hex(tokenStr);
        redisUtils.set(userKey, tokenMd5, JwtConstant.EXPIRE_SECOND);

        return LoginUserDTO.builder()
                .artistId(userDBO.getArtistId())
                .artistName(artistName)
                .userId(userDBO.getId())
                .userName(userDBO.getName())
                .userAvatarUrl(userDBO.getAvatarUrl())
                .unionId(unionId)
                .token(tokenStr)
                .provider(provider.toString())
                .build();
    }
}
